import {data} from './main.js';
import {drag} from './Utils.js';
import * as force from './force.js';
import * as d3 from 'https://unpkg.com/d3@6?module';

data.levelOrder = [];

export function start() {
    completeTree();
    presetData();
    addControlPoint();
    addLinks()
    return ForceGraph();
}

function completeTree() {
    data.nodes.forEach(node => {
        data.levelOrder.push(node.level);
    });
    data.levelOrder.sort()
    // 加一层
    data.nodes.forEach(node => {
        if (node.childNum > 1) {
            if (node.level === data.levelOrder[data.levelOrder.length - 1]) {
                let newLevel = String.fromCharCode(node.level.charCodeAt(0) + 1)
                data.levelOrder.push(newLevel)
                data.nodes.push({level: newLevel})
            }
        }
    })
    // 遍历每个层级
    data.levelOrder.forEach((level, index) => {
        let totalChildNum = 0;
        if (index > 0) {
            data.nodes.forEach(node => {
                if (node.level === data.levelOrder[index - 1]) {
                    totalChildNum += node.childNum;
                }
            });
        }
        let currentLevelCount = data.nodes.filter(node => node.level === level).length;
        while (currentLevelCount < totalChildNum) {
            data.nodes.push({level: level});
            currentLevelCount++;
        }
        data.nodes.filter(node => node.level === level).forEach((node, index) => {
            if (undefined === node.childNum) {
                if (node.level === data.levelOrder[data.levelOrder.length - 1]) {
                    node.childNum = 0;
                } else {
                    node.childNum = 1;
                }
            }
            if (undefined === node.id) {
                node.id = `${node.level}${index + 1}`;

            }
        });
    });

    // data.添加links
    data.links = data.links || [];
    let nextLevelStartIndex = 0;
    data.levelOrder.forEach((level, levelIndex) => {
        if (levelIndex < data.levelOrder.length - 1) { // 如果不是最后一个层级
            let nextLevel = data.levelOrder[levelIndex + 1]; // 下一个层级
            let nextLevelNodes = data.nodes.filter(node => node.level === nextLevel);
            let currentLevelNodes = data.nodes.filter(node => node.level === level);
            let currentLinkIndex = 0; // 当前链接索引
            currentLevelNodes.forEach((node, nodeIndex) => {
                for (let i = 0; i < node.childNum; i++) {
                    if (currentLinkIndex < nextLevelNodes.length) {
                        let targetNode = nextLevelNodes[currentLinkIndex];
                        data.links.push({
                            source: `${level}${nodeIndex + 1}`,
                            target: `${nextLevel}${currentLinkIndex + 1}`,
                        });
                        currentLinkIndex++;
                    }
                }
            });
            nextLevelStartIndex += nextLevelNodes.length; // 更新下一个层级的起始索引
        }
    });

    return data.nodes
}

function presetData() {
    data.nodes.forEach(node => {
        node.levelIndex = node.level.charCodeAt(0) - 'a'.charCodeAt(0);
        node.group ||= node.levelIndex + 1;
        node.lastBlew = 3;
        node.size ||= 77 * (node.levelIndex + 2) / (node.levelIndex + 1) / 2
        node.density ||= 1;
        node.mass ||= node.size * node.density || 1;
    });
}

function addControlPoint() {
    if (!data.nodes.some(node => node.root === true)) {
        data.nodes.push({
            size: 15, mass: 9999999, color: "red", group: -1, root: true, id: "root",
            fx: innerWidth / 2, fy: innerHeight / 2,
        });
    }
}


function addLinks() {
    data.links.forEach(link => {
        const sourceNode = data.nodes.find(node => node.id === link.source);
        const targetNode = data.nodes.find(node => node.id === link.target);
        link.width = Math.sqrt((sourceNode.size || 0) + (targetNode.size || 0) / 2);
    });
}

function ForceGraph() {
    let defaults = {
        nodeId: d => d.id,
        nodeGroups: null,
        nodeFill: "currentColor",
        nodeStroke: "#fff",
        nodeStrokeWidth: 5.5,
        nodeStrokeOpacity: 0.5,
        nodeSize: d => d.size ? d.size : 40,
        linkStroke: "#999",
        linkStrokeOpacity: 0.6,
        linkStrokeWidth: l => l.width || 3,
        linkStrokeLinecap: "round",
        colorSchema: [
            "#7FC6A4", "#C5A3FF", "#FFC0CB", "#98FF98", "#FFFACD",
            "#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd",
            "#FFEA00", "#00B0FF", "#FF1744", "#1DE9B6", "#D500F9",
            "#003366", "#0B6623", "#4B0082", "#800000", "#4E4E50",
            "#708090", "#B87333", "#556B2F", "#C08081", "#4B0082",
            "#87CEEB", "#E6E6FA", "#7CFC00", "#FFE5B4", "#B0E0E6",
        ],
        width: 1920,
        height: 800,
        invalidation: null,
    };
    Object.assign(data, defaults);
    data.groups = d3.map(data.nodes, d => d?.group || 0);
    data.linkStrokeWidths = typeof data.linkStrokeWidth !== "function" ? null : d3.map(data.links, data.linkStrokeWidth);
    data.linkStrokes = typeof data.linkStroke !== "function" ? null : d3.map(data.links, data.linkStroke);
    data.colors = d3.scaleOrdinal(d3.sort(data.groups), data.colorSchema);
    const simulation = d3.forceSimulation(data.nodes)
        .velocityDecay(0.005)
        .alphaDecay(0.0)
        .alphaMin(0)
        // .force("center", d3.forceCenter())
        // .force("charge", d3.forceManyBody()
        //         .strength(d => {
        //             console.log(d.mass);
        //             return d.mass * 1;
        //         }))
        .force("board", force.board)
        .force("link", force.link)
        .force("collide", force.collide)
        // .force("hang", force.hang)
        .force("wind", force.wind)
        // .force("G",force.G)
        .on("tick", ticked)

    function ticked() {
        node
            .attr("cx", d => d.root !== undefined ? d.fx : d.x)
            .attr("cy", d => d.root !== undefined ? d.fy : d.y);

        link
            .attr("x1", d => d.source.x)
            .attr("y1", d => d.source.y)
            .attr("x2", d => d.target.x)
            .attr("y2", d => d.target.y)

        labels
            .each(function (d) {
                d.tbox = this.getBBox();
            })
            .attr("x", d => d.x - d.tbox.width / 2)
            .attr("y", d => d.y + d.tbox.height / 2)

    }

    const svg = d3.create("svg")
        .attr("width", data.width)
        .attr("height", data.height)
        .attr("viewBox", [-data.width / 2, -data.height / 2, data.width, data.height])
        .attr("style", "max-width: 100%; height: auto; height: intrinsic;");

    const link = svg.append("g")
        .attr("stroke", typeof data.linkStroke !== "function" ? data.linkStroke : null)
        .attr("stroke-opacity", data.linkStrokeOpacity)
        .attr("stroke-width", typeof data.linkStrokeWidth !== "function" ? data.linkStrokeWidth : null)
        .attr("stroke-linecap", data.linkStrokeLinecap)
        .selectAll("line")
        .data(data.links)
        .join("line")
        .call(drag(simulation))

    const node = svg.append("g")
        .attr("fill", data.nodeFill)
        .attr("stroke", data.nodeStroke)
        .attr("stroke-opacity", data.nodeStrokeOpacity)
        .attr("stroke-width", data.nodeStrokeWidth)
        .selectAll("circle")
        .data(data.nodes)
        .join("circle")
        .attr("r", data.nodeSize)
        .call(drag(simulation))

    const labels = svg.append("g")
        .attr("class", "content")
        .selectAll("text")
        .data(data.nodes)
        .join("text")
        .text(d => d.content ? d.content : "")
        .attr("x", 0)
        .attr("y", 0)


    if (data.linkStrokeWidths) link.attr("stroke-width", ({index: i}) => data.linkStrokeWidths[i]);
    if (data.linkStrokes) link.attr("stroke", ({index: i}) => data.linkStrokes[i]);
    if (data.groups) node.attr("fill", ({index: i}) => data.colors(data.groups[i]));
    if (data.invalidation != null) data.invalidation.then(() => simulation.stop());

    function intern(value) {
        return value !== null && typeof value === "object" ? value.valueOf() : value;
    }


    return Object.assign(svg.node(), {scales: {color: data.colors}});
}

